/*
 * @Copyright: 2017 www.yyfax.com Inc. All rights reserved.
 */
package com.yyfax.pay.order.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.transaction.Transactional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import com.yyfax.commons.json.JacksonUtil;
import com.yyfax.commons.lang.DateUtil;
import com.yyfax.framework.exception.YYException;
import com.yyfax.framework.yyfax.CryptUtil;
import com.yyfax.framework.yyfax.DesensitizationUtil;
import com.yyfax.framework.yyfax.enums.CurrencyEnum;
import com.yyfax.framework.yyfax.enums.YesNoEnum;
import com.yyfax.pay.common.constants.PayConstants;
import com.yyfax.pay.common.enums.NotifyTaskTypeEnum;
import com.yyfax.pay.common.enums.PayRspCodeEnum;
import com.yyfax.pay.common.enums.PayinBatchStepEnum;
import com.yyfax.pay.common.enums.PayinStepEnum;
import com.yyfax.pay.common.enums.PaymentEnum;
import com.yyfax.pay.common.enums.QueryTaskTypeEnum;
import com.yyfax.pay.common.exception.YYPayException;
import com.yyfax.pay.config.data.service.MerchantConfigService;
import com.yyfax.pay.config.model.po.MerchantConfigPO;
import com.yyfax.pay.order.data.dao.PayinBatchOrderLogDao;
import com.yyfax.pay.order.data.service.PayinBatchOrderLogService;
import com.yyfax.pay.order.data.service.PayinBatchOrderService;
import com.yyfax.pay.order.data.service.PayinOrderLogService;
import com.yyfax.pay.order.data.service.PayinOrderService;
import com.yyfax.pay.order.model.po.PayinBatchOrderLogPO;
import com.yyfax.pay.order.model.po.PayinBatchOrderPO;
import com.yyfax.pay.order.model.po.PayinOrderLogPO;
import com.yyfax.pay.order.model.po.PayinOrderPO;
import com.yyfax.pay.order.model.to.payin.PayinBatchOrderContent;
import com.yyfax.pay.order.model.to.payin.PayinBatchOrderYYContent;
import com.yyfax.pay.order.model.to.payin.PayinBatchResultNotifyParams;
import com.yyfax.pay.order.model.to.payin.PayinBatchResultNotifyParams.PayinBatchResultNotify;
import com.yyfax.pay.order.model.to.payin.PayinBatchResultNotifyParams.PayinBatchResultNotify.PayinResultNotify;
import com.yyfax.pay.order.model.to.payin.PayinBatchSnapshotVo;
import com.yyfax.pay.order.model.to.payin.PayinBatchVo;
import com.yyfax.pay.order.model.to.payin.PayinOrderBatchParams;
import com.yyfax.pay.order.model.to.payin.PayinOrderBatchParams.PayinOrder;
import com.yyfax.pay.order.model.to.payin.PayinOrderVO;
import com.yyfax.pay.order.model.to.payin.QueryBatchPayinOrderYYContent;
import com.yyfax.pay.order.model.to.payin.QueryBatchPayinOrderYYContent.QueryPayinOrder;
import com.yyfax.pay.third.service.ThirdPayService;

/**
 * 代扣业务服务
 * 
 * @author maojian
 * @date 2017-04-25
 * @since v1.0.0
 */
@Service
public class PayinBatchService {

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private PayinOrderService payinOrderService;

	@Autowired
	private PayinBatchOrderService payinBatchOrderService;

	@Autowired
	private PayinBatchOrderLogDao payinBatchOrderLogDao;

	@Autowired
	private PayinOrderLogService payinOrderLogService;

	@Autowired
	private AdapterService adapterService;

	@Autowired
	private PayCommonService payCommonService;

	@Autowired
	private PayinBatchOrderLogService payinBatchOrderLogService;

	@Autowired
	private MerchantConfigService merchantConfigService;

	@Autowired
	private PayConstants payConstants;

	/**
	 * 保存代扣订单，并返回支付快照
	 * 
	 * @author maojian
	 * @date 2017-04-25
	 * @since v1.0.0
	 * @param PayinBatchVo
	 * @return
	 * @throws Exception
	 */
	@Transactional
	public PayinBatchSnapshotVo saveBatchPayinOrder(PayinOrderBatchParams params) throws Throwable {
		PayinBatchSnapshotVo payinBatch = new PayinBatchSnapshotVo();
		// 1.保存批量代扣订单
		PayinBatchOrderPO po = new PayinBatchOrderPO();
		BeanUtils.copyProperties(params, po);
		po.setCurrency(CurrencyEnum.RMB.getValue());
		po.setActPaymentCode(params.getPaymentCode());
		String payinBatchOrderId = null;
		try {
			payinBatchOrderId = payinBatchOrderService.saveAndReturnBusiKey(po);
		} catch (DuplicateKeyException e) {
			logger.debug("【saveBatchPayinOrder】error:{},{},批量代扣订单已存在", params.getMerchantCode(), params.getOrderId(), e);
			// 幂等操作
			payinBatch = payinBatchOrderService.querySnapshotByMerchantCodeAndOrderId(params.getMerchantCode(), params.getOrderId());
			return payinBatch;
		}

		// 2.保存详细代扣信息
		List<PayinOrder> dataList = params.getDataList();
		List<PayinOrderVO> payinOrderVOs = new ArrayList<>();
		for (PayinOrder payinOrder : dataList) {
			PayinOrderVO payinOrderVO = new PayinOrderVO();
			PayinOrderPO payinOrderPO = new PayinOrderPO();
			BeanUtils.copyProperties(po, payinOrderPO);
			BeanUtils.copyProperties(payinOrder, payinOrderPO);
			BeanUtils.copyProperties(payinOrderPO, payinOrderVO);
			payinOrderVOs.add(payinOrderVO);
			try {
				// 卡号脱敏，加密
				String certNo = payinOrderPO.getCertNo();
				payinOrderPO.setCertNoCip(CryptUtil.encrypt(certNo));
				payinOrderPO.setCertNo(DesensitizationUtil.convertIdCard(payinOrderPO.getCertNo()));
				String payerCardNo = payinOrderPO.getPayerCardNo();
				payinOrderPO.setPayerCardNo(DesensitizationUtil.convertIdCard(payerCardNo));
				payinOrderPO.setPayerCardNoCip(CryptUtil.encrypt(payerCardNo));
				String payinOrderId = payinOrderService.saveAndReturnBusiKeyByBatch(payinOrderPO);
				payinOrderVO.setPayinOrderId(payinOrderId);
			} catch (DuplicateKeyException e) {
				logger.error("【saveBatchPayinOrder】error:{},{},{},批量代扣中单笔代扣订单已存在", params.getOrderId(), payinOrderPO.getMerchantCode(),
						payinOrderPO.getOrderId(), e);
				// 批次下单笔已存在直接抛出异常
				throw new YYException(PayRspCodeEnum.PAY_BATCH_ITEM_CONFLICT, PayRspCodeEnum.PAY_BATCH_ITEM_CONFLICT.getDisplay());
			}
		}
		po.setPayinBatchOrderId(payinBatchOrderId);
		BeanUtils.copyProperties(po, payinBatch);
		payinBatch.setPayinOrderVOs(payinOrderVOs);
		return payinBatch;
	}

	/**
	 * 发起代扣
	 * 
	 * @author maojian
	 * @date 2017-04-25
	 * @since v1.0.0
	 * @param PayinOrderBatchParams
	 * @return
	 * @throws Exception
	 */
	public PayinBatchOrderContent payinBatch(PayinBatchVo payinBatchOrder) throws Exception {
		String merchantCode = payinBatchOrder.getMerchantCode();
		String orderId = payinBatchOrder.getOrderId();

		PayinBatchOrderContent content = new PayinBatchOrderContent();

		ThirdPayService service = adapterService.adapterPayService(PaymentEnum.fromValue(payinBatchOrder.getActPaymentCode()));

		PayinBatchOrderYYContent payinBatchOrderYYContent = service.payinBatchOrder(payinBatchOrder);

		// 写入批量代扣日志,批量请求太大，不存放
		savePayinBatchOrderLog(payinBatchOrder, PayinBatchStepEnum.STEP1_SUBMIT_ORDER, payinBatchOrderYYContent.getReqUrl(),
				payinBatchOrderYYContent.getReqContent(), payinBatchOrderYYContent.getRspContent(), payinBatchOrderYYContent.getResultCode(),
				payinBatchOrderYYContent.getResultMsg(), payinBatchOrderYYContent.getOutBatchId(), null, YesNoEnum.NO);

		// 批量代扣查询任务： 主动查询
		String startTime = DateUtil.datetime2Str(DateUtil.addMinute(new Date(), payConstants.getPayoutBatchQueryMinuteDelay())); // 默认为15分钟
		if (!PayRspCodeEnum.PAY_CODE_PAY_FAIL.isValue(payinBatchOrderYYContent.getResultCode())) {
			payCommonService.saveQueryTask(merchantCode, orderId, null, QueryTaskTypeEnum.PAYIN_BATCH, payinBatchOrder.getPayinBatchOrderId(),
					payinBatchOrder.getActPaymentCode(), startTime, null, payConstants.getQueryTaskMax(), "");
		}
		

		// 批量代扣查询任务： 对账查询

//		String startTimeReconciliation = DateUtil.datetime2Str(DateUtil.set(DateUtil.addDay(new Date(), payConstants.getPayoutBatchCheckDayDelay()),
//				payConstants.getPayoutBatchCheckHour(), payConstants.getPayoutBatchCheckMinute(), payConstants.getPayoutBatchCheckSecond())); // 设置次日9点
//		payCommonService.saveQueryTask(merchantCode, orderId, null, QueryTaskTypeEnum.PAYIN_BATCH_RECONCILIATION,
//				payinBatchOrder.getPayinBatchOrderId(), payinBatchOrder.getActPaymentCode(), startTimeReconciliation, null,
//				payConstants.getQueryTaskMax(), "");


		BeanUtils.copyProperties(payinBatchOrder, content);
		content.setResultCode(payinBatchOrderYYContent.getResultCode());
		content.setResultMsg(payinBatchOrderYYContent.getResultMsg());

		return content;
	}

	/**
	 * 保存支付日志
	 * 
	 * @author buyi
	 * @date 2017-04-17 17:31:44
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param step
	 * @param reqUrl
	 * @param reqContent
	 * @param rspContent
	 * @param serialNo
	 * @param resultCode
	 * @param resultMsg
	 * @param isClose
	 * @throws Exception
	 */
	public PayinBatchOrderLogPO savePayinBatchOrderLog(PayinBatchVo payinBatchOrderPO, PayinBatchStepEnum step, String reqUrl, String reqContent, String rspContent,
			Integer resultCode, String resultMsg, String outBatchId, String remark, YesNoEnum isClose) throws Exception {
		PayinBatchOrderLogPO logPO = new PayinBatchOrderLogPO();
		BeanUtils.copyProperties(payinBatchOrderPO, logPO);
		logPO.setPaymentCode(payinBatchOrderPO.getActPaymentCode());

		logPO.setStep(step.getValue());
		logPO.setReqUrl(reqUrl);
		logPO.setReqContent(reqContent.length()>500?reqContent.substring(500):reqContent);
		logPO.setRspContent(rspContent.length()>500?rspContent.substring(500):rspContent);
		logPO.setResultCode(resultCode);
		logPO.setResultMsg(resultMsg);
		logPO.setOutBatchId(outBatchId);
		logPO.setRemark(remark);
		logPO.setClosed(isClose.getValue());
		payinBatchOrderLogService.save(logPO);

		return logPO;
	}

	/**
	 * 向第三方通道查询订单,并将查询结果更新到本地
	 * 
	 * @author maojian
	 * @date 2017-05-3 09:02:01
	 * @since v1.0.0
	 * @param payOrder
	 * @throws Exception
	 */
	public PayinBatchSnapshotVo queryPayinBatchOrder(String orderId, String merchantCode) throws Exception {
		// 1.查询订单封装VO
		PayinBatchSnapshotVo payinBatchOrderVO = payinBatchOrderService.querySnapshotByMerchantCodeAndOrderId(merchantCode, orderId);
		if(payinBatchOrderVO == null){
			return null;
		}
		
		PayinBatchOrderLogPO lastPayinBatchOrderLog = payinBatchOrderVO.getOrderLastResultStep();
		if (payinBatchOrderVO == null) {
			return null;
		}

		// 2.同步结果或者查询结果存在，则直接返回结果
		if (null != lastPayinBatchOrderLog && (PayinBatchStepEnum.STEP3_PAY_QUERY.isValue(lastPayinBatchOrderLog.getStep())
				|| PayinBatchStepEnum.STEP4_PAY_ASYN_RESULT.isValue(lastPayinBatchOrderLog.getStep()))) {
			return payinBatchOrderVO;
		}

		// 3.主动查询
		PaymentEnum payment = PaymentEnum.fromValue(payinBatchOrderVO.getPaymentCode());
		ThirdPayService thirdPayService = adapterService.adapterPayService(payment);

		QueryBatchPayinOrderYYContent queryContent = thirdPayService.queryBatchPayinOrder(payinBatchOrderVO);

		// 查询失败
		if (queryContent == null) {
			throw new YYPayException(PayRspCodeEnum.COMMON_ERR0R_NET_ERROR, "查询第三方订单失败:" + orderId);
		}

		// 处理中 或 请求超时
		if (PayRspCodeEnum.PAY_CODE_DEALING.isValue(queryContent.getResultCode())
				|| PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.isValue(queryContent.getResultCode())) {
			throw new YYPayException(PayRspCodeEnum.COMMON_ERR0R_NET_ERROR, "查询第三方订单结果:" + PayRspCodeEnum.fromValue(queryContent.getResultCode()));
		}

		// 更新批量订单结果信息
		modifyPayinBatchOrder(payinBatchOrderVO, queryContent, PayinBatchStepEnum.STEP3_PAY_QUERY, "主动查询");

		// 主动查询结束，生成异步通知
		createPayinBatchResultNotify(payinBatchOrderVO);

		return payinBatchOrderVO;
	}

	/**
	 * 生成批量代扣异步通知
	 * 
	 * @author maojian
	 * @date 2017-04-22 15:39:51
	 * @since v1.0.0
	 * @param payinBatchOrderVO
	 * @throws Exception
	 */
	private void createPayinBatchResultNotify(PayinBatchSnapshotVo vo) throws Exception {
		// 获取商户信息
		MerchantConfigPO merchantConfigPO = merchantConfigService.queryByMerchantCode(vo.getMerchantCode());

		// 封装异步通知消息
		PayinBatchResultNotifyParams payinBatchResultNotifyParams = new PayinBatchResultNotifyParams();

		PayinBatchResultNotify params = new PayinBatchResultNotify();
		params.setResultCode(vo.getOrderLastResultStep().getResultCode());
		params.setResultMsg(vo.getOrderLastResultStep().getResultMsg());
		params.setMerchantCode(vo.getMerchantCode());
		params.setPaymentCode(vo.getPaymentCode());
		params.setOrderId(vo.getOrderId());
		params.setBatchDate(vo.getBatchDate());
		params.setBatchNo(vo.getBatchNo());
		params.setSuccessCount(vo.getSuccessCount());
		params.setSuccessAmount(vo.getSuccessAmount());
		params.setFailCount(vo.getFailCount());
		params.setFailAmount(vo.getFailAmount());
		params.setUnknownCount(vo.getUnknownCount());
		params.setUnknownAmount(vo.getUnknownAmount());

		List<PayinOrderVO> payinOrderVOs = vo.getPayinOrderVOs();

		List<PayinResultNotify> dataList = new ArrayList<PayinResultNotify>();

		if (payinOrderVOs != null) {
			for (PayinOrderPO payinOrderVO : payinOrderVOs) {
				PayinResultNotify payinResultNotify = new PayinResultNotify();
				BeanUtils.copyProperties(payinOrderVO, payinResultNotify);
				payinResultNotify.setSign(payinResultNotify.sign(merchantConfigPO.getSignKey()));

				dataList.add(payinResultNotify);
			}
		}

		params.setDataList(dataList);

		payinBatchResultNotifyParams.setSign(params.sign(merchantConfigPO.getSignKey()));
		payinBatchResultNotifyParams.setParams(params);

		// 插入异步通知日志
		payCommonService.saveNotifyTask(vo.getMerchantCode(), vo.getOrderId(), null, NotifyTaskTypeEnum.PAYOUT_BATCH, vo.getPayinBatchOrderId(),
				vo.getNotifyUrl(), JacksonUtil.noemptyMapper.toJson(payinBatchResultNotifyParams), DateUtil.datetime2Str(new Date()), null,
				payConstants.getMaxNo(), "");

	}

	/**
	 * 更新批量代付信息
	 * 
	 * @author maojian
	 * @date 2017-04-22 13:34:58
	 * @since v1.0.0
	 * @param payinBatchOrderVO
	 * @param queryContent
	 * @param step
	 * @param isClose
	 * @param remark
	 * @params childOderClose 代付订单关闭状态
	 * @throws Exception
	 */
	private void modifyPayinBatchOrder(PayinBatchSnapshotVo payinBatchOrderVO, QueryBatchPayinOrderYYContent queryContent, PayinBatchStepEnum step,
			String remark) throws Exception {

		// 更新批量代付结果信息
		payinBatchOrderVO.setSuccessCount(queryContent.getSuccessCount());
		payinBatchOrderVO.setSuccessAmount(queryContent.getSuccessAmount());
		payinBatchOrderVO.setFailCount(queryContent.getFailCount());
		payinBatchOrderVO.setFailAmount(queryContent.getFailAmount());
		payinBatchOrderVO.setUnknownCount(queryContent.getUnknownCount());
		payinBatchOrderVO.setUnknownAmount(queryContent.getUnknownAmount());

		PayinBatchOrderPO payinBatchOrderPO = new PayinBatchOrderPO();
		BeanUtils.copyProperties(payinBatchOrderVO, payinBatchOrderPO);
		payinBatchOrderService.modify(payinBatchOrderPO);

		// 保存批量代付日志
		PayinBatchOrderLogPO payinBatchOrderLogPO = savePayinBatchOrderLog(payinBatchOrderVO, step, queryContent.getReqUrl(),
				queryContent.getReqContent(), queryContent.getRspContent(), queryContent.getResultCode(), queryContent.getResultMsg(),
				null, remark, YesNoEnum.NO);

		// 更新批量代付快照信息
		payinBatchOrderVO.getOrderLogPOs().add(payinBatchOrderLogPO);

		// 保存详细代付订单日志
		List<QueryPayinOrder> queryPayinOrders = queryContent.getQueryPayinOrders();
		if (queryPayinOrders != null) {
			for (QueryPayinOrder queryPayinOrder : queryPayinOrders) {
				PayinOrderVO payinOrderVO = payinBatchOrderVO.getPayinOrderVO(queryPayinOrder.getPayinOrderId());
				payinOrderVO.setSerialNo(queryPayinOrder.getSerialNo());
				// 插入日志
				PayinOrderLogPO payinOrderLogPO = savePayinOrderLog(payinOrderVO, PayinStepEnum.STEP3_PAY_QUERY, null, null, null,
						queryPayinOrder.getSerialNo(), queryPayinOrder.getResultCode(), queryPayinOrder.getResultMsg(), "批量代扣结果查询", YesNoEnum.YES);

				// 更新代付快照信息
				payinOrderVO.getOrderLogPOs().add(payinOrderLogPO);
			}
		}
	}

	/**
	 * 保存支付日志
	 * 
	 * @author buyi
	 * @date 2017-04-17 17:31:44
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param step
	 * @param reqUrl
	 * @param reqContent
	 * @param rspContent
	 * @param serialNo
	 * @param resultCode
	 * @param resultMsg
	 * @param isClose
	 * @throws Exception
	 */
	public PayinOrderLogPO savePayinOrderLog(PayinOrderPO payoutOrderPO, PayinStepEnum step, String reqUrl, String reqContent, String rspContent,
			String serialNo, Integer resultCode, String resultMsg, String remark, YesNoEnum isClose) throws Exception {
		PayinOrderLogPO logPO = new PayinOrderLogPO();
		BeanUtils.copyProperties(payoutOrderPO, logPO);
		logPO.setPaymentCode(payoutOrderPO.getActPaymentCode());

		logPO.setStep(step.getValue());
		logPO.setReqUrl(reqUrl);
		logPO.setReqContent(reqContent);
		logPO.setRspContent(rspContent);
		logPO.setSerialNo(serialNo);
		logPO.setResultCode(resultCode);
		logPO.setResultMsg(resultMsg);
		logPO.setRemark(remark);
		logPO.setClosed(isClose.getValue());
		payinOrderLogService.save(logPO);

		return logPO;
	}
}
